// Copyright (c) 2023 CHANGLEI. All rights reserved.

// ignore_for_file: avoid_types_on_closure_parameters

part of 'arith.dart';

const _priority = 1 << 31 - 1;

/// Created by changlei on 2023/6/7.
///
/// 函数
final class Math with Describable implements Symbols, Operable {
  /// 函数
  const Math(this._math);

  final Callable _math;

  /// 优先级
  int get priority => _math.priority;

  /// 执行函数
  num call(List<num> values) {
    return _math.call(values);
  }

  @override
  String toDescribe([bool isTight = false]) {
    return _math.symbol;
  }

  @override
  bool operator ==(Object other) =>
      identical(this, other) || other is Math && runtimeType == other.runtimeType && _math == other._math;

  @override
  int get hashCode => _math.hashCode;

  /// 解析
  static Math? maybeOf(String s) => BuiltInMath.values.maybeOf(s);

  /// 解析
  static Math of(String s) => ArgumentError.checkNotNull(maybeOf(s));
}

/// 函数
enum BuiltInMath implements Callable {
  /// sqrt
  sqrt,

  /// cos
  cos,

  /// sin
  sin,

  /// exp
  exp,

  /// log
  log,

  /// acos
  acos,

  /// atan
  atan,

  /// tan
  tan,

  /// abs
  abs,

  /// floor
  floor,

  /// ceil
  ceil,

  /// round
  round,

  /// truncate
  truncate,

  /// floorToDouble
  floorToDouble,

  /// ceilToDouble
  ceilToDouble,

  /// roundToDouble
  roundToDouble,

  /// truncateToDouble
  truncateToDouble,

  /// toDouble
  toDouble,

  /// toInt
  toInt,

  /// pow
  pow,

  /// atan2
  atan2,

  /// min
  min,

  /// max
  max,

  /// remainder
  remainder,

  /// clamp
  clamp,

  /// gcd
  gcd,

  /// modInverse
  modInverse,

  /// modPow
  modPow,

  /// toSigned
  toSigned,

  /// toUnsigned
  toUnsigned;

  @override
  int get priority => _priority;

  @override
  num call(List<num> values) {
    final function = switch (this) {
      sqrt => math.sqrt,
      cos => math.cos,
      sin => math.sin,
      exp => math.exp,
      log => math.log,
      acos => math.acos,
      atan => math.atan,
      tan => math.tan,
      abs => (num x) => x.abs(),
      floor => (num x) => x.floor(),
      ceil => (num x) => x.ceil(),
      round => (num x) => x.round(),
      truncate => (num x) => x.truncate(),
      floorToDouble => (num x) => x.floorToDouble(),
      ceilToDouble => (num x) => x.ceilToDouble(),
      roundToDouble => (num x) => x.roundToDouble(),
      truncateToDouble => (num x) => x.truncateToDouble(),
      toDouble => (num x) => x.toDouble(),
      toInt => (num x) => x.toInt(),
      pow => math.pow,
      atan2 => math.atan2,
      min => math.min,
      max => math.max,
      remainder => (num a, num b) => a.remainder(b),
      clamp => (num a, num b, num c) => a.clamp(b, c),
      gcd => (num a, num b) => a.toInt().gcd(b.toInt()),
      modInverse => (num a, num b) => a.toInt().modInverse(b.toInt()),
      modPow => (num a, num b, num c) => a.toInt().modPow(b.toInt(), c.toInt()),
      toSigned => (num a, num b) => a.toInt().toSigned(b.toInt()),
      toUnsigned => (num a, num b) => a.toInt().toUnsigned(b.toInt()),
    };
    return Function.apply(function, values) as num;
  }

  @override
  String toString() => symbol;
}

/// 自定义函数
sealed class Callable {
  /// 自定义函数
  const factory Callable(
    String symbol,
    num Function(List<num> values) call, [
    int priority,
  ]) = _Callable;

  /// 计算
  num call(List<num> values);

  /// 优先级
  int get priority;
}

/// 自定义函数
class _Callable implements Callable {
  /// 自定义函数
  const _Callable(this.name, this._call, [this.priority = _priority]);

  final String name;

  final num Function(List<num> values) _call;

  @override
  final int priority;

  @override
  num call(List<num> values) => _call(values);

  @override
  bool operator ==(Object other) =>
      identical(this, other) ||
      other is _Callable &&
          runtimeType == other.runtimeType &&
          name == other.name &&
          _call == other._call &&
          priority == other.priority;

  @override
  int get hashCode => name.hashCode ^ _call.hashCode ^ priority.hashCode;

  @override
  String toString() => symbol;
}

/// 名称
extension CallableSymbol on Callable {
  /// 名称
  String get symbol {
    return switch (this) {
      BuiltInMath(:final name) => name,
      _Callable(:final name) => name,
    };
  }

  Callable _copyWith({
    String? symbol,
    num Function(List<num> values)? call,
    int? priority,
  }) {
    return Callable(
      symbol ?? this.symbol,
      call ?? this.call,
      priority ?? this.priority,
    );
  }
}

/// 解析自定义函数
extension CallableOf on Iterable<Callable> {
  /// 解析
  Math? maybeOf(String s) {
    for (final callable in this) {
      final symbol = callable.symbol;
      if (symbol == s || symbol == '`$s`') {
        return Math(callable._copyWith(symbol: s));
      }
    }
    return null;
  }
}
